Conversation
…o + ship @decocms/next Squashed history of PR #307 (originally merged to a branch named 7.x, then migrated to v7 — see below). Three phases plus release automation: Phase 1 — Monorepo split ========================= Splits the single `@decocms/start` package into a 5-package Bun workspace with a one-way dependency graph: packages/live → @decocms/live (CMS core: cms/, matchers/, sdk/, hooks/, types/, admin schema composition — named "live" not "runtime": @decocms/runtime already exists on npm as a real, unrelated, actively-published package at v2.3.1) packages/admin → @decocms/admin (admin protocol handlers, depends on live) packages/cli → @decocms/cli (codegen scripts + Fresh→ TanStack migration tooling, depends on live) packages/tanstack → @decocms/tanstack (TanStack Start + Cloudflare Workers binding: workerEntry, cmsRoute, vite plugin, daemon; depends on live + admin) packages/next → @decocms/next (new) Root-caused and fixed the real bug motivating the split: dist/tsup bundling caused module-state duplication across package boundaries, which is why an earlier attempt at this shipped as v5.2.2 and had to be reverted. This split ships plain .ts source exports with no bundling step. Phase 2 — @decocms/next (Next.js App Router binding) ====================================================== New package mirroring @decocms/tanstack's capabilities for Next.js: SectionRenderer, DeferredSectionBoundary (RSC Suspense streaming), client-only-forced section rendering, DecoPageRenderer, createDecoPage, DecoRootLayout, and Route Handler re-exports of the admin protocol. Validated against tanstack-smoke and next-smoke fixtures. Follow-up cleanup ================== Refreshed README.md, CLAUDE.md, and deep-dive docs for the new architecture. Replaced GAP_ANALYSIS.md + GAP_ANALYSIS_V2.md with docs/known-gaps.md. Audited all ~30 .cursor/skills/ directories: deleted 8 stale ones, merged 2 near-duplicate pairs, path-fixed the rest. Forward-ported 3 fixes that landed on main while this branch was in flight (x-deco-matchers-override, @title/@ignore JSDoc on app loaders, a vite dev-watcher delta-apply fix for a real production incident). Release automation (v7 branch, ci/release.yml + .releaserc.v7.json) ====================================================================== main keeps releasing the legacy @decocms/start (v*.*.* tags, unchanged config). v7 releases the 5 new @decocms/* packages independently, under a separate tag namespace (live-v*.*.*) via a dedicated semantic-release config — deliberately NOT sharing main's `branches` array, since main's @decocms/start and the 5 new packages are unrelated npm packages with no real version relationship. Two hard-won, non-obvious fixes baked into this config, both costly to rediscover: 1. The branch cannot be named like a semver range ("7.x"). Semantic- release auto-classifies any `N.x`-shaped branch NAME as a "maintenance" branch — regardless of how it's written in `branches` config, since classification runs on the resolved branch name, not the config-side glob. Maintenance branches are structurally required to have a LOWER version than a companion "release"-type branch, which is backwards from what we want (v7 should be newer than legacy main). No in-branches-array trick escapes this (confirmed empirically against real semantic-release source: lib/branches/index.js's per-type branchesValidator). Renamed the branch 7.x -> v7 to sidestep it entirely — "v7" doesn't match the range-shape pattern, so it's just a normal branch with a seeded starting tag (live-v7.0.0, on a commit unique to this branch's history, NOT shared with main — sharing it caused semantic-release to compute an impossible `>=7.0.0 <7.0.0`-shaped range once, since both branches would "see" the same tag as their own last release). v7 still needs a companion branch in `branches` (semantic-release requires ≥1 non-maintenance-shaped branch even for a single-branch config) — used a genuine orphan placeholder branch (decocms-live-legacy-anchor, pinned at a fixed v0.0.1 seed tag) rather than main, since main's own fluctuating computed version was ALSO capping v7's valid range in confusing ways. 2. semantic-release's `--dry-run` only skips its OWN native git-tag creation step. It calls `plugins.prepare()` and `plugins.publish()` UNCONDITIONALLY either way, and @semantic-release/exec's prepare/publish hooks don't check dry-run themselves. A naive exec-based publishCmd would attempt a REAL `npm publish` during a "dry run". .releaserc.v7.json's publishCmd checks a `$DRY_RUN` env var (set by the workflow from the workflow_dispatch dry_run input) before calling npm — verified this is a real, not theoretical, risk: an early version of this config without the guard reached a real `npm publish --access public` for @decocms/admin during testing (caught immediately; failed on 404 only because publish permissions aren't provisioned yet, not because of the dry-run guard — that guard didn't exist at the time). Added a workflow_dispatch trigger with a dry_run input (default true) specifically so the version computation and publish plan can be inspected in real CI logs before trusting an automatic push-triggered release — this is genuinely a one-way door (5 packages that have never been published before) and deserves that scrutiny. First verified computation: 7.0.1. Known follow-up, not yet resolved: whatever npm credentials are currently configured for this repo's Actions (NODE_AUTH_TOKEN via actions/setup-node) return 404 (not authorized to CREATE new packages) for @decocms/admin and @decocms/cli — confirmed via the real publish attempt above. The actual first release will need proper npm token provisioning with create-package rights on the @decocms org before it can succeed. Verified: all 5 packages typecheck clean and pass their full test suites via vitest (live 455, admin 15, next 10, cli 369, tanstack 59 — 908 tests total). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…decision)
Same rationale/mechanics as the earlier @decocms/runtime -> @decocms/live
rename: renamed packages/live/ -> packages/blocks/ (git mv, preserving
file history) and every @decocms/live import + bare packages/live path
reference (102 files) to @decocms/blocks.
Also renamed the release-line naming to match: tagFormat
live-v${version} -> blocks-v${version} in .releaserc.v7.json, and
the placeholder companion branch decocms-live-legacy-anchor ->
decocms-blocks-legacy-anchor (new orphan branch + blocks-v0.0.1 seed
tag pushed; old decocms-live-legacy-anchor branch and live-v* tags
left in place for now, not yet deleted).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…decocms/cli -> @decocms/blocks-cli Disambiguates both packages as part of the @decocms/blocks family — 'admin' and 'cli' alone are generic enough to be confusing on their own, unlike @decocms/tanstack and @decocms/next which are already self-explanatory as framework bindings (left unchanged). Renamed packages/admin/ -> packages/blocks-admin/ and packages/cli/ -> packages/blocks-cli/ (git mv, preserving file history), plus every @decocms/admin and @decocms/cli import + bare packages/admin and packages/cli path reference (65 files total) to the new names. Cross-package workspace dependencies (packages/next and packages/tanstack both depend on @decocms/admin; packages/tanstack also depends on @decocms/cli) updated in the same pass. bin script entries in blocks-cli/package.json use relative paths, unaffected by the rename. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…in/blocks-cli siblings vitest run --root ../.. packages/blocks was matching as a path PREFIX, not an exact directory — since packages/blocks-admin and packages/blocks-cli both start with the substring 'packages/blocks', running @decocms/blocks's own test script incorrectly pulled in both siblings' test files too (35 -> 58 files, 455 -> 839 tests). A trailing slash forces an exact directory boundary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e provisioned 3+ push-triggered runs during development created real git tags before failing on npm 404 (CI's NODE_AUTH_TOKEN lacks create-package rights on @decocms org), each requiring manual tag cleanup. Not removing the capability, just the automatic push trigger — re-add 'v7' to the push branches list once a proper npm Automation token is configured, then use workflow_dispatch for the actual first release rather than relying on the next push to catch it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…citness Renamed packages/next/ -> packages/nextjs/ and examples/next-smoke/ -> examples/nextjs-smoke/ (git mv, preserving file history), plus every @decocms/next import + bare packages/next and examples/next-smoke path reference to the new names. Also proactively added trailing slashes to every package's 'vitest run --root ../.. packages/X' test script (not just the ones with a live collision) — packages/blocks's own test script silently picked up packages/blocks-admin and packages/blocks-cli's tests too in the previous rename (path-prefix match, not exact directory), and nextjs' name is now a substring-prefix risk for any future packages/nextjs-* addition. Trailing slash forces an exact directory boundary in all cases. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NODE_AUTH_TOKEN is now a granular access token with 2FA bypass and read/write access to the decocms org + all packages. All 5 v7 packages were bootstrapped to 7.0.0 via a manual local publish (confirmed live on npm) using the same token; future commits release through this trigger normally. Also added .npmrc to .gitignore (holds npm auth tokens during local publish sessions) and fixed a stale examples/next-smoke comment missed in the nextjs rename. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…red token Removes the NODE_AUTH_TOKEN dependency entirely — each of the 5 v7 packages now has this repo's release.yml configured as its trusted publisher on npmjs.com. Adds an explicit npm upgrade step since Trusted Publishing needs npm >=11.5.1 and Node 22's bundled npm is older. Also deleted the now-unused NODE_AUTH_TOKEN repo secret. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ication npm Trusted Publishing (OIDC) auto-generates sigstore provenance attestations and cross-checks package.json's repository.url against the actual GitHub repo the OIDC token came from — publish fails with E422 if repository is missing. Adds repository.url + directory to all 5 v7 packages, pointing at https://github.com/decocms/blocks (this repo's current name). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.0.1 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
The declared ^1.28.0 floor is too loose: otel.ts uses ATTR_DEPLOYMENT_ENVIRONMENT_NAME, which doesn't exist until 1.41.1 (verified directly against package tarballs — absent in 1.40.0's stable_attributes.js, present in 1.41.1's). deco-start's own tree happens to resolve a high-enough version so this never surfaced locally, but a consumer (faststore-fila) with a different dependency tree resolved 1.40.0 and hit a hard TS2724 type error on this package's own otel code. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.0.2 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
…mports The full @decocms/blocks/cms barrel (index.ts) re-exports loader.ts and resolve.ts, which transitively import node:async_hooks and node:fs/promises. Bundling ANY export from that barrel for a browser target — even one with zero Node dependencies itself, like getResolvedComponent — drags the whole module graph in, since ES imports are evaluated per-file, not per-export. Turbopack rejects this outright in production builds; webpack has historically let it through uncaught. Found via faststore-fila's migration to this package: a Client Component importing only getResolvedComponent failed Turbopack's production build. New @decocms/blocks/cms/client entry point exports just the verified-safe subset: registry.ts (component lookups — imports only React types), sectionMixins.ts (goes through useDevice.ts -> requestContext.ts, whose only node:async_hooks dependency is already swapped for a browser stub via this package's existing "browser" export condition), and schema.ts (zero imports). Deliberately excludes loader.ts, resolve.ts, sectionLoaders.ts, loadDecofileDirectory.ts, blockSource.ts, and applySectionConventions.ts — resolver/storage concerns that only make sense server-side. Verified with a real esbuild browser-target bundle, not just tsc: client.ts bundles clean with zero node: references; index.ts genuinely fails the same bundle with real node:async_hooks resolution errors, proving both that the original bug is real and that the split fixes it. Kept as an automated regression test (client.browserBundle.test.ts) — shells out to the esbuild CLI binary rather than using its JS API, since the JS API's internal TextEncoder/Uint8Array invariant check fails across Vitest's module-isolation VM boundary (confirmed the invariant holds under plain `bun -e`; it's a realm mismatch, not a real environment problem). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.1.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
…arrel split DecoPageRenderer.tsx (packages/tanstack/src/hooks/) is client-bundled (useState/useEffect/Suspense/lazy) and only used runtime imports that are all in the new cms/client subset (getResolvedComponent, getSectionOptions, getSectionRegistry, getSyncComponent, preloadSectionModule, setResolvedComponent, SectionOptions) — a real consumer of the split published as @decocms/blocks@7.1.0. Its DeferredSection/ResolvedSection type-only imports stay on the full @decocms/blocks/cms barrel, since type imports are erased at compile time regardless of source path, and neither type is in cms/client anyway. Swept the docs/skills that describe this import path for staleness: - CLAUDE.md: added a cms/client row to the package-exports table with guidance on when to use it vs the full barrel; also fixed the Package column still saying "runtime"/"next" from before the @decocms/runtime->live->blocks and @decocms/next->nextjs renames. - import-mapping.md (deco-next-package-migration skill): getResolvedComponent's row still described the dev-diagnostic page's view component as "a client component" — no longer true, it was converted to a Server Component in faststore-fila specifically because of this bug. Added the concrete fix-by-case breakdown and pointed future readers at cms/client for the common case. - docs/hydration-and-ssr-migration.md: the DecoPageRenderer syncThenable fix sketch didn't show an import statement; added a note that getResolvedComponent there needs cms/client, not the full barrel, since it's the exact component this session found and fixed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.1.1 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
…ability.ts sdk/observability.ts re-exports instrumentWorker from ./otel, but otel.ts imported configureMeter/configureTracer/getActiveSpan back from ./observability — a real cycle (observability.ts -> otel.ts -> observability.ts). Confirmed via a real Rollup warning surfaced while building casaevideo-tanstack: "will likely lead to broken execution order" when the two modules land in different chunks. Fix: otel.ts now imports those three functions directly from ../middleware/observability (where they're actually defined — sdk/observability.ts just re-exports them), the same module it already imported METRIC_METADATA from. Breaks the cycle without changing any public API. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.1.2 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Captures the 2026-07-07 design session: split-by-platform package taxonomy (blocks-* for framework, apps-* for integrations, nextjs/tanstack for framework bindings), the matcher/flags/UI-component duplication found and how to resolve it, lockstep release integration, and what happens to the old apps-start repo. Design only — no implementation yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed with the user: blocks/hooks (existing subpath, already houses similar UI primitives like RenderSection/LazySection) over a new blocks/commerce subpath, to minimize new export-surface additions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…onorepo 15 tasks: shared migration tooling, apps-commerce (foundational), UI component relocation to blocks/hooks, matcher/flags cleanup in blocks, apps-website (reduced scope), then apps-vtex/shopify/magento/algolia/ salesforce/resend/blog, end-to-end verification against faststore-fila, and a deprecation notice on the old apps-start repo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…admin/apps/autoconfig mapping Verified in Task 1 Step 2 by comparing apps-start's registry.ts doc comment (references autoconfigApps()) against packages/blocks-admin/src/apps/autoconfig.ts, which exports an equivalent autoconfigApps(blocks, registry) and documents the old call site directly. Matches the mapping already in this repo's CLAUDE.md.
Moves apps-start's commerce/ (types, app-types, resolve, manifest-utils, utils/*, sdk/*) and root registry.ts into a new @decocms/apps-commerce package, excluding commerce/components/ (Task 3 moves those UI components elsewhere). Rewrites @decocms/start/* imports via scripts/migrate-apps-import.mjs; the registry.ts doc comment's remaining @decocms/start references are updated by hand to the confirmed @decocms/blocks-admin/apps/autoconfig mapping. Test files that lived in __tests__/ subdirs in apps-start are flattened to sibling *.test.ts files to match this repo's co-located-test convention; their relative imports were fixed accordingly (../foo -> ./foo). Known gap, not resolved here: registry.ts's APP_REGISTRY entries dynamically import platform mod modules (./shopify/mod, ./vtex/mod, ./resend/mod, ./blog/mod) that lived alongside commerce/ in apps-start's single package. Now that each platform is a separate @decocms/apps-<platform> package depending one-way on @decocms/apps-commerce, resolving those imports would require the reverse edge (apps-commerce -> apps-shopify -> apps-commerce), violating the monorepo's one-way dependency rule. Left as @ts-expect-error stopgaps with an explanatory comment pending a plan-level decision (e.g. a site-level registry aggregator, or non-statically-typed string entries) rather than guessing a fix. Verified: bun install clean; packages/apps-commerce typecheck + test clean (5 files, 50 tests); whole-workspace typecheck and test clean across all 6 packages (blocks, apps-commerce, blocks-admin, blocks-cli, nextjs, tanstack). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…m entries Task 2's implementation surfaced a real conflict: apps-start's registry.ts was a single static array whose entries relatively-imported every platform's mod.ts, only possible because everything lived in one package. Split by platform, no apps-* package can hold that array without depending on every other apps-* package, violating the one-way rule. User decision: apps-commerce/registry keeps only the shared types; each platform package with a registrable app (vtex, shopify, resend, blog) exports its own single-entry registry from its own package, and sites compose their own array explicitly. Updates Tasks 7/8/12/13's scope accordingly (documented here; each task's own section gets the concrete addition when reached). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…stry types Resolves the circular-dependency gap flagged in the previous commit: registry.ts no longer holds a static APP_REGISTRY array whose entries dynamically imported sibling platform mods (./shopify/mod, ./vtex/mod, ./resend/mod, ./blog/mod) that can't resolve from this package without creating a reverse apps-commerce -> apps-<platform> -> apps-commerce dependency edge. Per decision: each platform package with a registrable app now exports its own single-entry registry from its own ./registry subpath (Tasks 7, 8, 12, 13 — not this task). apps-commerce/registry.ts keeps only the shared AppRegistryEntry / AppRegistry types those per-platform registries will import. The @decocms/apps-commerce package.json's "./registry" export subpath is unchanged. Verified: packages/apps-commerce typecheck + test clean (5 files, 50 tests); whole-workspace typecheck clean across all 6 packages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t commerce/components) Moves Image, Picture, and the JsonLd SEO components from apps-start's commerce/components/ into @decocms/blocks/hooks (existing subpath, per the design's resolved decision to reuse it rather than add blocks/commerce). Image and Picture were already self-contained (no commerce-type imports) and moved unchanged; ported Image's Vitest suite alongside it. JsonLd.tsx imported Product/ProductListingPage/BreadcrumbList/etc. from apps-start's commerce types module (now @decocms/apps-commerce/types), which @decocms/blocks cannot depend on (one-way rule: apps-* depends on blocks, never the reverse). Each JsonLd function only reads a small, flat subset of those schema.org types (name/url/sku/image[].url/ offers.price/aggregateRating.ratingValue, etc.) — the original code already didn't fully trust the nominal Product.offers type either (it cast to `Offer[] | AggregateOffer`). Replaced the import with local, minimal structural types (JsonLdProduct, JsonLdProductListingPage, JsonLdBreadcrumbList) that describe just what's read; any real apps-commerce Product/ProductListingPage/BreadcrumbList value is a structural superset and can be passed in without a cast. No `JsonLd` symbol exists in the source file (only ProductJsonLd, PLPJsonLd, BreadcrumbJsonLd, seoMetaTags), and Picture.tsx has no default export (named Picture/Source) — the barrel exports the components' actual names rather than the brief's placeholder `default as X` shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Composes createSiteSetup, applySectionConventions, loadBlocks (all route-handler-safe) with loadDecofileDirectory for filesystem decofile loading, and lazily imports @decocms/blocks-admin for meta/renderShell/ previewWrapper so the eager import graph never touches module-scope client-React. Returns a memoized ensureSetup() for Task 6's dispatcher and fila's Task 10 to call before every admin/route-handler request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
setupPromise ??= (...) permanently cached a rejected first attempt, so one transient failure (fs blip during loadDecofileDirectory, flaky meta() fetch) would poison the warm serverless instance forever. Now the memo is cleared on rejection so the next ensureSetup() call retries, while the triggering call still rejects with the original error. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds createDecoRouteHandlers() to routeHandlers.ts: a single GET/POST dispatcher for app/deco/[[...deco]]/route.ts that routes /deco/decofile, /deco/meta, /deco/render, /deco/invoke/*, and /deco/previews/* (rebuilt to the /live/previews/* prefix handleRender parses) to the existing blocks-admin handlers, running an optional setup() before each request and 404-ing unknown paths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…oke branch The catch-all dispatcher wired the same handler to GET and POST with no method check on invoke/*. handleInvoke has no auth and falls back to a ?props=<json> query string for GET, so an unauthenticated <img> tag on a third-party page could trigger mutating VTEX actions cross-site (GET is a CORS simple request, no preflight). The per-URL invokePOST export this dispatcher replaces was POST-only for exactly this reason — restore it, and 405 non-GET on meta (read-only schema endpoint) for symmetry. Also pins the previews-rebuild's `new Request(rebuilt, request)` body- forwarding semantics with a test, and documents why a plain-object init would throw "duplex option is required" on a future refactor. That test needs Node's native Request (jsdom's fetch polyfill drops the body on Request-as-init), so routeHandlers.test.ts now runs under @vitest-environment node, matching this package's setup.test.ts precedent. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lePackages) Adds packages/nextjs/src/config.cjs (CJS, requireable from a CJS next.config.js despite the package being "type": "module") exporting withDeco() and DECO_REWRITES, plus a src/config.d.cts declaration and a "./config" exports map entry. withDeco() prepends the three Studio rewrites (/.decofile, /live/_meta, /live/previews/:path*) that Task 6's dispatcher expects, ahead of any user rewrites in array or object form, and dedupes transpilePackages with the @decocms packages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…s/createNextSetup + README recipe Migrates examples/nextjs-smoke off its ad-hoc rewrites/route files onto the three new @decocms/nextjs surfaces built in Tasks 5-7 (withDeco, createDecoRouteHandlers, createNextSetup), and adds packages/nextjs/README.md as the copy-paste recipe a new Next.js site follows. While validating end-to-end against a real `next build`/`next dev`, found and fixed two real bugs surfaced by the migration (not just fixture wiring): - routeHandlers.ts's dispatch derived the requested action solely from a `/deco/`-prefixed pathname, on the assumption that a next.config rewrite hands route handlers the destination path. Verified empirically (dev and `next start`) that Next.js instead hands `request.url`/`nextUrl.pathname` as the ORIGINAL, pre-rewrite path — so every rewritten protocol URL (`/.decofile`, `/live/_meta`, `/live/previews/*`) 404'd. `resolveAction` now accepts both forms; added regression tests hitting the rewrite-source paths directly (the prior tests only ever constructed already-/deco/* Requests, so they'd have kept passing against the broken dispatcher). - Picture.tsx (reachable from DecoRootLayout/SectionRenderer via the hooks/index.ts barrel) imports createContext/useContext without "use client", which crashes Next's react-server module-graph check for any page that imports the root barrel. Added "use client", same precedent as SectionErrorFallback.tsx's existing fix for the same class of issue. Also dropped a stray console.log left in blocks-admin's handleRender. Verification: `examples/nextjs-smoke` builds clean; dev server curls of /.decofile, /live/_meta, and / (CMS-resolved Hero content) all 200; repo-wide `bun run typecheck && bun run test` green across all packages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…he block comment Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…enance path npm 12.0.0 (published 2026-07-08T21:06Z, ~1h before our release run) fails every 'npm publish' with "Cannot find module 'sigstore'" from libnpmpublish's provenance.js. Two release runs died on it; the morning's 7.3.1 release on the same runner image succeeded because @latest still resolved to 11.18.0 then. Pin the major; revisit after npm 12 stabilizes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.4.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tifacts live in the framework's folder)
Flips the default output path of four blocks-cli generators from
src/server/{cms,admin}/ to .deco/, so generated framework artifacts
live alongside .deco/blocks instead of scattered under app source:
- generate-blocks -> .deco/blocks.gen.ts (+ sibling .deco/blocks.gen.json,
derived from --out-file by extension swap)
- generate-loaders -> .deco/loaders.gen.ts
- generate-sections -> .deco/sections.gen.ts
- generate-schema -> .deco/meta.gen.json
generate-invoke is the one exception and keeps its default at
src/server/invoke.gen.ts: unlike the other four (inert data/metadata
snapshots the framework loads at runtime), it emits actual app
server-function code that TanStack Start's build-time compiler
transforms (createServerFn().handler() -> RPC stubs), so it belongs
in src/ with the rest of the compiled app code. Documented inline.
Legacy guard: when no explicit --out/--out-file flag is passed AND
the OLD default file still exists on disk, each generator prints a
one-line stderr warning (lib/legacyArtifact.ts) naming both paths and
telling the caller to move the file and update its importers, then
writes to the NEW default anyway. An explicit flag skips the guard
entirely (deliberate choice, no warning). Every flipped generator
mkdir -p's the new output directory before writing.
Updated in the same commit (ships in the same lockstep release):
- packages/tanstack/src/vite/plugin.js dev-mode watcher, which called
generateBlocks()/generate-schema.ts relying on the old defaults
- packages/blocks-cli/scripts/migrate.ts's scaffolding templates
(setup.ts, commerce-loaders.ts, knip-config.ts) that imported from
or referenced the old default paths
- packages/nextjs README + setup.ts JSDoc: scripts no longer need
--out/--out-file, and the recipe now imports generated artifacts
through a `deco/*` tsconfig path alias -> .deco/* instead of a
relative import, since src/deco/setup.ts isn't adjacent to the
site-root .deco/ directory
- packages/blocks-admin's "unknown handler" error message and
packages/blocks/src/setup.ts JSDoc, which pointed at the old path
examples/nextjs-smoke and examples/tanstack-smoke construct their
setup inline and don't invoke any of these generators, so they needed
no changes.
Tests: extended generate-sections.test.ts and generate-schema.test.ts
with subprocess fixtures covering the new default path, the legacy
warning (both paths + guidance text on stderr, new default still
written), and the explicit-flag no-warning case.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
generate-blocks/loaders/sections/schema now default their output to
.deco/ instead of src/server/{cms,admin}/ (commit 36ebf2a). Update the
two migration-skill docs that hardcoded the old paths in troubleshooting
commands and the setup.ts template's generated-artifact imports.
generate-invoke's src/server/invoke.gen.ts default is unchanged and was
left as-is everywhere it's referenced.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.5.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
The spawnSync-tsx fixture tests take ~6s under full-suite parallel load, tripping vitest's 5s default — 2 flaky failures on whole-monorepo runs that never reproduced in isolated package runs. test: type on purpose — no release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… inside Vite Root cause of the long-standing non-fatal "[deco] blocks bootstrap failed: TypeError: generateBlocks is not a function" cold-start warning on lebiscuit-tanstack: tsx 4.22.0-4.22.4 has a loader-hook state bug (fixed upstream in 4.22.5, "isolate hook state per async module.register() registration"). Inside a Vite dev-server process, tsImport(@decocms/blocks-cli/generate-blocks) resolves the module correctly but returns an EMPTY namespace — no rejection, which is why every out-of-band reproduction (plain node, same parent URL, same tsx copy) passed while the in-process call failed. Sites whose lockfiles froze 4.21.0 (baggagio, casaevideo, fila) never saw it; lebiscuit's lockfile froze 4.22.0. Verified in both directions on lebiscuit: tsx 4.21.0 -> bootstrap succeeds; 4.22.0 -> empty namespace; 4.23.0 -> succeeds. blocks-cli's tsx range was ^4.19.0, so any fresh install could land in the broken window — floor it at ^4.22.5. The tanstack vite plugin's loadGenModule also now fails with an actionable message (naming the tsx window and the upgrade command) when it sees an empty namespace, instead of the bare "generateBlocks is not a function" that a site's own hoisted-broken-tsx lockfile pin would still surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.5.1 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
…src/ Smoke-tested moving it to .deco/ on a real site: TanStack Start's client stub transform handles the new path fine, but the server half can't resolve the split module back to an executable handler — every /_serverFn call 500s (unhandled, no stack) while the storefront and typecheck stay green. Reverted; same probe back under src/ returns 200 with a real VTEX orderForm. docs-only commit, no release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reload endpoint is destructive (posted body fully replaces the in-memory registry) and its dev-bypass predicate already changed once (import.meta.env.DEV -> NODE_ENV). Five cases: no header, no token configured (fail-closed not fail-open), wrong token, NODE_ENV unset — all 401 with registry untouched — plus the correct-token 200 path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ocms/apps imports The migrate/scaffold/post-cleanup pipeline still emitted (or recommended) @decocms/start and the @decocms/apps monolith, neither of which exist for 7.x consumers -- every newly scaffolded site and every deco-post-cleanup --fix run against an already-migrated site was writing code that doesn't resolve. Repoints every emitted import/dependency/npx-command at the current split packages (@decocms/blocks, @decocms/blocks-admin, @decocms/tanstack, @decocms/blocks-cli, @decocms/apps-vtex/-shopify/ -commerce/-magento), verified against each package's actual exports map. Also adds the missing @decocms/tanstack "./sdk/cookiePassthrough" export (the file existed but was never exported) and broadens two post-cleanup DETECT regexes to recognize both the old and new package names so audits of freshly-fixed sites don't false-negative. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.5.2 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
…plies to directories isExcludedCodegenFile() required a prefix before the marker word, so a file literally named test.ts/spec.tsx/stories.ts (no prefix) was not excluded from section/loader codegen scans. Anchor the marker word to either the start of the filename or a preceding dot so bare names are caught too, while keeping testimonials.tsx/generic.ts (marker word embedded mid-identifier) included. Also: generate-schema.ts's findTsxFiles walk applied the exclusion check to directory entries as well as files, so a directory literally named e.g. foo.gen.ts would be silently skipped instead of walked. generate-sections.ts already applied the predicate to files only; normalize generate-schema.ts to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ainModule() The CLI arg-parsing block (argv → SITE_NAMESPACE/SECTIONS_REL/etc.) and the legacy-artifact check ran unconditionally at module scope, so merely importing this file for its pure exports (definitionIdForPath, applyWidgetFormat, typeToJsonSchema — done by generate-schema.test.ts) ran an fs.existsSync check against the importing process's cwd and could print a legacy-artifact warning as an import-time side effect unrelated to what the test wanted to exercise. generateMeta() and the final write are already reached only inside `if (isMainModule())`; move the arg parsing + legacy check into that same guard (declaring the argv-derived vars with real defaults via `let` so generateMeta() still closes over them). The CLI/subprocess behavior is unchanged — verified via the existing generate-schema.test.ts subprocess suite, which drives real `npx tsx generate-schema.ts [args]` invocations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
generate-sections.ts's emitted .deco/sections.gen.ts had no trailing newline in --registry mode (the last pushed line was "};" with no separator after it) and a doubled blank line before the registry doc comment (both the loadingFallbacks block and the registry block unconditionally pushed a "" separator). The same double-blank pattern also showed up between the header comment and the SectionMetaEntry interface whenever a site has no sync/ LoadingFallback sections. Collapse any run of blank lines to a single one and normalize to exactly one trailing newline at the final write, regardless of which branches ran. This changes generated bytes: every consuming site will see a one-time regeneration diff on its next build. No action needed — sites regenerate .deco/sections.gen.ts on every build, so the new formatting self-applies. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…port fix-up
Step 4 of upgradeSectionRenderer() checked `!result.includes("RenderSection")`
to decide whether to add the missing `import { RenderSection } from
"@decocms/blocks/hooks"`. Every `<RenderSection` JSX usage introduced by
steps 2/3 above already contains the substring "RenderSection", so this
check was always false and the import was never added — a nested-section
file that gained `<RenderSection>` JSX but had no matching import would
silently ship broken.
Check for the actual import statement instead of the JSX substring. Export
upgradeSectionRenderer for direct testing (mirrors the existing
writeImportedLibShims export) and add a regression fixture: a
`.Component`/`.props` call converts to `<RenderSection>` JSX with no prior
import, and the import must now be added.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(a) transforms/jsx.ts and phase-cleanup.ts coordinate through the literal
"@decocms/start/hooks" import specifier: jsx.ts deliberately writes that
pre-split package path as a same-run handoff, and phase-cleanup.ts's
upgradeSectionRenderer() matches it later in the same migration run to
rewrite it to the real "@decocms/blocks/hooks" target. Neither side named
its counterpart, so a future reorder of the transform/cleanup phases could
silently desync them. Add a one-line comment on both ends.
(b) templates/package-json.ts assumes every @decocms/* package version is
lockstep (a single getLatestVersion("@decocms/blocks", ...) call covers
blocks/blocks-admin/blocks-cli/tanstack/apps-*). Document what to change if
that assumption ever breaks: each `${frameworkVersion}` usage would need
its own getLatestVersion() call.
No behavior change — comments only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…istent package The migration's import-rewrite rules mapped "apps/shopify/hooks/$1" to "@decocms/apps-shopify/hooks/$1", but that target has never existed: the current @decocms/apps-shopify package ships no src/hooks/ directory and no ./hooks/* export (confirmed via package.json and `git log --diff-filter=A -- '**/shopify/hooks/**'` returning nothing across all history, including the pre-split @decocms/apps monolith it was migrated from wholesale in c7604df). The legacy migration reference (.agents/skills/deco-to-tanstack-migration/references/platform-hooks/README.md) confirms Shopify's useCart/useUser/useWishlist were always meant to become site-local no-op stubs, not package hooks — and templates/hooks.ts's generateHooks() still scaffolds exactly those three at src/hooks/use{Cart,User,Wishlist}.ts for every non-VTEX platform today. Mirror the VTEX rule shape: route the three known hook names to the scaffolded local files (same as "apps/vtex/hooks/useUser" → "~/hooks/useUser" above), and drop the generic fallback entirely rather than pointing it at a package that was never real. No shopify hook name other than these three has ever existed, so an unmatched "apps/shopify/hooks/*" import now falls through to the general "apps/*" catch-all (which removes the import line) — a loud break at the usage site instead of a phantom unresolvable module. Also adds this file's first dedicated test suite (transforms/imports.ts had none), covering the shopify hooks fix above and splitDecoHooksImports (the @deco/deco/hooks → useDevice/useScript split post-process), including the mixed-import and order-of-operations cases. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
createNextSetup's option surface (packages/nextjs/src/setup.ts) had a single happy-path test plus a memoization/retry test. Add direct coverage for the remaining paths: blocksDir as a real string path (tmp dir with one JSON decofile), options.blocks winning over blocksDir on an overlapping key, renderShell/previewWrapper/meta reaching @decocms/blocks-admin's setters (mocked, matching the existing routeHandlers.test.ts pattern in this same package — @decocms/blocks-admin is a heavier graph than the CMS core), productionOrigins/customMatchers/onResolveError/onDanglingReference passthrough to createSiteSetup, and extend() receiving the merged loaded blocks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🎉 This PR is included in version 7.5.3 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Summary
The
v7line:@decocms/start(single legacy package) split into a bun-workspace monorepo of 14 lockstep-versioned packages, releasing independently from this branch via semantic-release (blocks-v*tags, npm OIDC trusted publishing). Published through 7.5.3; verified end to end against four production storefronts.mainkeeps shipping legacy@decocms/start6.x until this line becomes primary.Package map
@decocms/blocks— CMS block resolution, section registry, matchers, portable SDK (framework-agnostic)@decocms/blocks-admin— Studio handlers:/live/_meta,/.decofile, invoke, render/previews@decocms/blocks-cli— generators (blocks/sections/loaders/schema/invoke), site migration scaffolder@decocms/tanstack(Vite plugin, worker entry, daemon),@decocms/nextjs(App Router glue)@decocms/apps-{vtex,shopify,magento,salesforce,algolia,commerce,website,blog,resend}— one package per platform, replacing the@decocms/appsmonolithLockstep versioning: one change releases all 14 packages at the same version — no cross-package matrix.
Highlights
Next.js glue tier (new)
createNextSetup()— one-call site bootstrap (decofile loading, section/matcher registration, admin schema, memoized with rejection-retry)createDecoRouteHandlers()— the entire Studio protocol from ONE catch-all route (app/deco/[[...deco]]/route.ts), accepting both pre-rewrite (/.decofile,/live/_meta,/live/previews/*) and/deco/*URL forms, with method gating (invoke POST-only — closes a GET/CSRF vector; meta GET-only)withDeco()next.config wrapper (CJS-requireable) — Studio-URL rewrites +transpilePackagespackages/nextjs/README.md;examples/nextjs-smokeconsumes all three.deco/is the framework's folder.deco/(blocks.gen.*,loaders.gen.ts,sections.gen.ts,meta.gen.json) with a legacy-artifact warning at old paths;generate-sections --registryemits a lazy import map (the non-Vite equivalent ofimport.meta.glob)invoke.gen.tsdeliberately stays insrc/— empirically proven: TanStack Start's server-fn resolution 500s when the compiled split module lives outside the app source tree (documented in the generator header with the repro)Correctness & portability
createContext(root barrels split; lazy contexts;"use client"boundaries) — route handlers ignore directives and run on React's react-server buildcreateServerFncompiler crashes under real npm installs fixed (createInvokeFnrewritten as a codegen-only marker;optimizeDeps.exclude)import.metasyntax in shipped source — CJS consumers (ts-jest) can compile the raw-TS packages*.test/spec/stories/gen.*files^4.22.5(4.22.0–4.22.4 loader-hook bug returned empty namespaces fromtsImportinside Vite dev)Migration scaffolder overhauled
~230 emitted imports updated from pre-split
@decocms/start/@decocms/appspaths to the 7.x package set (detect-patterns for legacy sites deliberately untouched); fixed a verify gate that would have failed 100% of migrations; Shopify hooks mapping repaired.Ops
Verified against production
Four real storefronts migrated and verified on this line (bagaggio, casaevideo, lebiscuit on TanStack/Workers; FILA FastStore on Next.js): production-vs-preview parity diffs show byte-identical
/.decofileand structurally identical/live/_metaschemas (modulo the deliberate ID renaming), full admin protocol serving, builds green, live deploy previews on every PR.Merge note
mainandv7are structurally incompatible (flatsrc/vspackages/*) — a normal merge cannot resolve this PR. Whenv7becomes primary: squash-merge or branch-swap, decided deliberately.